<template lang="html">
  <div @click="hideAllFilterPopover">
    <log-filter
      ref="accessLogFilter"
      :key="routeChangeIndex"
      :stream-list="streamList"
      :process-list="processList"
      :log-count="pagination.count"
      :loading="isChartLoading"
      :type="'accessLog'"
      @change="handleLogSearch"
      @date-change="handlePickSuccess"
      @reload="handleLogReload"
    />

    <div
      v-if="tableFormatFilters.length"
      class="table-filters"
    >
      <ul class="filter-list">
        <li
          v-for="(filter) of tableFormatFilters"
          :key="filter.value"
        >
          <span
            v-bk-tooltips="`${filter.key}：${filter.value}`"
            class="filter-value"
          >{{ filter.key }}: <i>{{ filter.value }}</i></span>
        </li>
      </ul>
      <span
        v-if="tableFilters.length"
        v-bk-tooltips.right="$t('清空筛选条件')"
        class="paasng-icon paasng-close-circle-shape clear-filters-btn"
        @click="handleClearFilters"
      />
    </div>

    <div
      ref="logMain"
      class="log-main"
    >
      <div class="log-fields">
        <p class="title">
          {{ $t('字段设置') }}
          <i
            v-bk-tooltips="$t('仅显示查询结果前200条数据包含的字段，没有出现的字段仍可以通过输入关键字查询')"
            class="paasng-icon paasng-info-circle tooltip-icon"
          />
        </p>
        <template v-if="fieldList.length">
          <bk-checkbox-group
            v-model="fieldSelectedList"
            class="field-list"
          >
            <bk-checkbox
              v-for="field of fieldList"
              :key="field.name"
              class="ps-field-checkbox"
              :value="field.name"
            >
              <span :title="field.name">{{ field.name }}</span>
            </bk-checkbox>
          </bk-checkbox-group>
        </template>
        <template v-else>
          <div
            class="bk-table-empty-block"
            style="margin-top: -40px;"
          >
            <span class="bk-table-empty-text">
              <table-empty empty />
            </span>
          </div>
        </template>
      </div>

      <div
        ref="logContent"
        class="log-content"
      >
        <div
          v-bkloading="{ isLoading: isChartLoading }"
          class="chart-box mb20"
          style="width: 100%;"
        >
          <div
            v-charts="chartData"
            style="width: 100%; height: 150px;"
          />
          <img
            v-if="!hasChartData"
            class="chart-placeholder"
            src="/static/images/chart-default.svg"
          >
        </div>

        <!-- 查询结果 start -->
        <div
          ref="tableBox"
          v-bkloading="{ isLoading: isLogListLoading }"
          class="table-wrapper log-table"
        >
          <bk-table
            :data="logs"
            size="small"
            :outer-border="false"
            :pagination="pagination"
            @page-change="pageChange"
            @page-limit-change="limitChange"
          >
            <div slot="empty">
              <table-empty
                :keyword="tableEmptyConf.keyword"
                :abnormal="tableEmptyConf.isAbnormal"
                @reacquire="getLogList"
                @clear-filter="clearFilterKey"
              />
            </div>
            <bk-table-column
              type="expand"
              width="30"
            >
              <template slot-scope="props">
                <div
                  class="log-wrapper"
                >
                  <div
                    v-for="(log, index) in logKeyList"
                    :key="index"
                    class="log-item-info"
                  >
                    <div
                      class="left-title"
                    >
                      {{ log }}:
                    </div>
                    <div
                      class="left-val"
                    >
                      {{ props.row[log] }}
                    </div>
                  </div>
                </div>
              </template>
            </bk-table-column>
            <bk-table-column
              label="Time"
              prop="timestamp"
            />
            <bk-table-column
              label="method"
              prop="method"
            />
            <bk-table-column
              label="path"
              prop="path"
            />
            <bk-table-column
              label="status_code"
              prop="status_code"
              :render-header="$renderHeader"
            />
            <bk-table-column
              label="response_time"
              prop="response_time"
              :render-header="$renderHeader"
            />
            <bk-table-column
              v-for="field in fieldSelectedList"
              :key="field"
              :label="field"
              :prop="field"
            />
          </bk-table>
        </div>
      </div>
    </div>
  </div>
</template>

<script>import moment from 'moment';
import xss from 'xss';
import pluginBaseMixin from '@/mixins/plugin-base-mixin';
import logFilter from '@/views/dev-center/app/engine/log/comps/log-filter.vue';
import { formatDate } from '@/common/tools';

const xssOptions = {
  whiteList: {
    'bk-highlight-mark': [],
  },
};
const logXss = new xss.FilterXSS(xssOptions);
const initEndDate = moment().format('YYYY-MM-DD HH:mm:ss');
const initStartDate = moment().subtract(1, 'hours')
  .format('YYYY-MM-DD HH:mm:ss');
const EXIST_LOG_KEY = ['timestamp', 'method', 'path', 'status_code', 'response_time'];
export default {
  components: {
    logFilter,
  },
  mixins: [pluginBaseMixin],
  data() {
    return {
      name: 'log-component',
      filterKeyword: '',
      contentHeight: 400,
      tabChangeIndex: 0,
      renderIndex: 0,
      renderFilter: 0,
      routeChangeIndex: 0,
      isLoading: true,
      tableMaxWidth: 700,
      isShowDate: true,
      lastScrollId: '',
      initDateTimeRange: [initStartDate, initEndDate],
      pagination: {
        current: 1,
        count: 0,
        limit: 20,
      },
      tableSortTypes: {},
      autoTimer: 0,
      fieldChecked: {},
      fieldPopoverShow: {},
      fieldCheckedList: [],
      isChartLoading: false,
      isLogListLoading: false,
      logList: [],
      streamLogList: [],
      searchFilterKey: [],
      tableFilters: [],
      streamLogFilters: [],
      processList: [],
      filterData: [],
      streamList: [],
      fieldList: [],
      logParams: {
        start_time: initStartDate,
        end_time: initEndDate,
        environment: '',
        process_id: '',
        stream: '',
        keyword: '',
        levelname: '',
        time_range: '1h',
      },
      fieldSelectedList: [],
      isFilter: false,
      logs: [],
      logKeyList: [],
      existFieldList: EXIST_LOG_KEY,
      tableEmptyConf: {
        isAbnormal: false,
        keyword: '',
      },
    };
  },
  computed: {
    chartData() {
      const data = this.$store.state.plugin.chartData;
      return data;
    },
    tableFormatFilters() {
      const results = [];
      const obj = {};
      // 重复key聚合
      this.tableFilters.forEach((item) => {
        if (!obj[item.key]) {
          obj[item.key] = [];
        }
        obj[item.key].push(item.value);
      });
      for (const key in obj) {
        results.push({
          key,
          value: obj[key].join(' | '),
        });
      }
      return results;
    },
    fieldOptions() {
      const options = {};
      const { fieldList } = this;
      fieldList.forEach((field) => {
        options[field.name] = [];
        if (field.name !== 'response_time') {
          field.list.forEach((item) => {
            options[field.name].push({
              text: item.text,
              value: item.id,
            });
          });
        }
      });
      return options;
    },
    fieldMap() {
      const obj = {};
      const { fieldList } = this;
      fieldList.forEach((field) => {
        obj[field.name] = field.id;
      });
      return obj;
    },
    hasChartData() {
      if (this.chartData.series.length && this.chartData.series[0].data.length) {
        return true;
      }
      return false;
    },
  },
  watch: {
    'logParams.keyword'(newVal, oldVal) {
      if (newVal === '' && oldVal !== '') {
        if (this.isFilter) {
          this.loadData(false);
          this.isFilter = false;
        }
      }
    },
    '$route.params'(newVal, oldVal) {
      if (newVal.id !== oldVal.id || newVal.moduleId !== oldVal.moduleId) {
        this.isLoading = true;
        // eslint-disable-next-line no-plusplus
        this.renderIndex++;
        // eslint-disable-next-line no-plusplus
        this.routeChangeIndex++;
        this.resetParams();
        this.loadData();
      }
    },
  },
  beforeRouteLeave(to, from, next) {
    clearInterval(this.autoTimer);
    this.resetParams();
    next(true);
  },
  created() {
    const query = this.$route.query || {};
    this.logParams = {
      start_time: query.start_time || initStartDate,
      end_time: query.end_time || initEndDate,
      environment: query.environment || '',
      process_id: query.process_id || '',
      stream: query.stream || '',
      keyword: query.keyword || '',
      levelname: query.levelname || '',
      time_range: query.time_range || '1h',
    };
  },
  mounted() {
    this.isLogListLoading = false;
    this.isLoading = false;
    this.init();
  },
  methods: {
    /**
             * 初始化入口
             */
    init() {
      this.isLoading = true;
      this.loadData();

      const winHeight = document.body?.scrollHeight || window.innerHeight;
      const height = winHeight - 400;
      if (height > 400) {
        this.contentHeight = height;
      }
      this.initTableBox();
      window.onresize = () => {
        this.initTableBox();
      };
    },

    initTableBox() {
      setTimeout(() => {
        const width = this.$refs.logMain.getBoundingClientRect().width - 220;
        this.$refs.tableBox.style.width = `${width}px`;
        this.$refs.tableBox.style.maxWidth = `${width}px`;
      }, 1000);
    },

    /**
             * 选择自定义时间，并确定
             */
    handlePickSuccess(params) {
      this.logParams = params;
      this.resetStreamLog();
      this.loadData();
    },

    /**
             * 清空查询参数
             */
    removeFilterParams() {
      if (this.$refs.bkSearcher && this.$refs.bkSearcher.removeAllParams) {
        this.$refs.bkSearcher.removeAllParams();
      }
    },

    toggleDetail(log) {
      log.isToggled = !log.isToggled;
      const list = JSON.parse(JSON.stringify(this.logList));
      this.logList.splice(0, this.logList.length, ...list);
      this.hideAllFilterPopover();
    },

    getParams() {
      return {
        start_time: this.logParams.start_time,
        end_time: this.logParams.end_time,
        time_range: this.logParams.time_range,
      };
    },

    /**
             * 构建过滤参数
             */
    getFilterParams() {
      const params = {
        query: {
          query_string: this.logParams.keyword,
        },
      };

      const filters = this.tableFilters;
      filters.forEach((filter) => {
        const filterKey = this.fieldMap[filter.key] || filter.key;
        if (!params.query.terms) {
          params.query.terms = {};
        }

        if (!params.query.terms[filterKey]) {
          params.query.terms[filterKey] = [];
        }

        params.query.terms[filterKey].push(filter.value);
      });

      if (this.logParams.process_id) {
        if (!params.query.terms) {
          params.query.terms = {};
        }
        params.query.terms.process_id = [this.logParams.process_id];
      }

      if (this.logParams.environment) {
        if (!params.query.terms) {
          params.query.terms = {};
        }
        params.query.terms.environment = [this.logParams.environment];
      }

      if (this.logParams.stream) {
        if (!params.query.terms) {
          params.query.terms = {};
        }
        params.query.terms.stream = [this.logParams.stream];
      }

      // response_time排序
      if (this.tableSortTypes.response_time) {
        params.sort = {
          response_time: this.tableSortTypes.response_time,
        };
      }
      return params;
    },

    /**
             * 加载所有数据
             */
    loadData(isLoadFilter = true) {
      this.tableSortTypes = {};
      this.$refs.accessLogFilter.setAutoLoad();
      this.pagination.current = 1;
      this.getLogList();
      this.getChartData();
    },

    /**
             * 重围搜索参数
             */
    resetParams() {
      this.initDateTimeRange = [initStartDate, initEndDate];
      this.lastScrollId = '';
      this.tableFilters = [];
      this.fieldSelectedList = [];
      this.fieldList = [];
      this.filterData = [];
      this.streamList = [];
      this.processList = [];
      this.logList = [];
      this.streamLogList = [];
      this.streamLogFilters = [];
      this.pagination = {
        current: 1,
        count: 0,
        limit: 20,
      };
      this.logParams = {
        start_time: initStartDate,
        end_time: initEndDate,
        environment: '',
        process_id: '',
        stream: '',
        keyword: '',
        time_range: '1h',
        levelname: '',
      };
      this.tableSortTypes = {};
    },

    /**
             * 关键字高亮
             * @param {String} text 匹配字符串
             */
    setKeywordHight(text) {
      const keywords = this.logParams.keyword.split(';');
      if (keywords.length) {
        keywords.forEach((keyword) => {
          keyword = keyword.trim();
          if (keyword) {
            const tpl = `<span class="ps-keyword-hightlight">${keyword}</span>`;
            const strReg = new RegExp(keyword, 'ig');
            text = text.replace(strReg, tpl);
          }
        });
        return text;
      }
      return text;
    },

    /**
             * 获取图表数据
             */
    async getChartData() {
      const params = this.getParams();
      const filter = this.getFilterParams();
      this.isLogListLoading = true;
      this.isChartLoading = true;
      try {
        const res = await this.$store.dispatch('plugin/getLogChartData', {
          pdId: this.pdId,
          pluginId: this.pluginId,
          pageParams: params,
          data: filter,
        });
        this.$store.commit('plugin/updateChartData', res);
      } catch (res) {
        this.$store.commit('plugin/updateChartData', {
          series: [],
          timestamps: [],
        });
      } finally {
        setTimeout(() => {
          this.isChartLoading = false;
          this.isLoading = false;
        }, 1000);
      }
    },

    /**
             * 修改页数目回调
             * @param  {Number} pageSize 每页数目
             */
    handlePageSizeChange(pageSize) {
      this.pagination.current = 1;
      this.pagination.limit = pageSize;
      this.$nextTick(() => {
        this.getLogList();
      });
    },

    handlePageChange(page = 1) {
      this.getLogList(page);
    },

    highlight(message) {
      return message.replace(/\[bk-mark\]/g, '<bk-highlight-mark>').replace(/\[\/bk-mark\]/g, '</bk-highlight-mark>');
    },

    pageChange(newPage) {
      this.pagination.current = newPage;
      this.getLogList(newPage);
    },

    limitChange(limit) {
      this.pagination.limit = limit;
      this.pagination.current = 1;
      this.getLogList(this.pagination.current);
    },

    /**
             * 获取日志数据
             * @param  {Number} page 第几页数据
             */
    async getLogList(page = 1) {
      const curPage = page || this.pagination.current;
      const params = this.getParams();
      // const pageSize = this.pagination.limit;
      params.limit = this.pagination.limit;
      params.offset = this.pagination.limit * (curPage - 1);
      const filter = this.getFilterParams();
      this.isLogListLoading = true;
      try {
        const res = await this.$store.dispatch('plugin/getAccessLogList', {
          pdId: this.pdId,
          pluginId: this.pluginId,
          pageParams: params,
          data: filter,
        });
        const data = res.logs;
        if (data.length) {
          this.logKeyList = Object.keys(res.logs[0]);
          // 字段列表
          this.fieldList = this.logKeyList.map((logKey) => {
            if (!this.existFieldList.includes(logKey)) {
              return {
                name: logKey,
              };
            }
            return false;
          });
          this.fieldList = this.fieldList.filter(item => item);
        }
        data.forEach((item) => {
          item.message = this.highlight(logXss.process(item.message));
          item.timestamp = this.formatTime(item.timestamp);
          if (item.detail) {
            for (const key in item.detail) {
              item.detail[key] = this.highlight(logXss.process(item.detail[key]));
            }
          }
          item.isToggled = false;
        });
        this.logs = data;
        this.logList.splice(0, this.logList.length, ...data);
        this.pagination.count = res.total;
        this.pagination.current = page;
        this.updateTableEmptyConfig();
        this.tableEmptyConf.isAbnormal = false;
      } catch (res) {
        this.tableEmptyConf.isAbnormal = true;
        this.pagination.count = 0;
      } finally {
        setTimeout(() => {
          this.isLogListLoading = false;
          this.isLoading = false;
        }, 500);
      }
    },

    handleLogSearch(params) {
      this.logParams = params;
      this.loadData(params.isDateChange);
      const query = Object.assign({}, this.$route.query, params);
      this.$router.push({
        // name: 'appLog',
        params: this.$route.params,
        query,
      });
    },

    handleLogReload(params) {
      this.loadData(false);
    },

    searchLog(params) {
      this.logParams.environment = '';
      this.logParams.process_id = '';
      this.logParams.stream = '';
      this.logParams.levelname = '';

      params.forEach((item) => {
        const type = item.id;
        const selectItem = item.value;
        this.logParams[type] = selectItem.id;
      });
      this.loadData(false);
    },

    handleFilterChange(field) {
      const list = [];
      for (const key in this.fieldChecked) {
        this.fieldChecked[key].forEach((field) => {
          const params = field.split(':');
          list.push({
            key: params[0],
            value: params[1],
          });
        });
      }
      this.tableFilters = list;
      this.fieldPopoverShow[field] = false;
      this.loadData(false);
    },

    handleCancelFilterChange(field) {
      this.fieldPopoverShow[field] = false;
      // eslint-disable-next-line no-plusplus
      this.renderIndex++;
    },

    handleRemoveFilter(filter, index) {
      this.tableFilters.splice(index, 1);
    },

    handleClearFilters() {
      this.tableFilters = [];
      // eslint-disable-next-line no-plusplus
      this.renderIndex++;
      // 清空筛选
      for (const key in this.fieldChecked) {
        this.fieldChecked[key] = [];
      }
      this.loadData(false);
    },

    handleExpandRow(row) {
      this.$refs.logList.toggleRowExpansion(row);
    },

    handleShowFilter(field) {
      if (!this.fieldPopoverShow[field]) {
        for (const key in this.fieldPopoverShow) {
          this.fieldPopoverShow[key] = false;
        }
        this.fieldPopoverShow[field] = true;
        this.filterKeyword = '';
        // eslint-disable-next-line no-plusplus
        this.renderIndex++;
      }
    },

    hideAllFilterPopover(el) {
      for (const key in this.fieldPopoverShow) {
        this.fieldPopoverShow[key] = false;
      }
      // eslint-disable-next-line no-plusplus
      this.renderFilter++;
    },

    handleHideFilter(field) {
      this.fieldPopoverShow[field] = false;
    },

    formatTime(time) {
      return time ? formatDate(time * 1000) : '--';
    },

    clearFilterKey() {
      this.$refs.accessLogFilter && this.$refs.accessLogFilter.clearKeyword();
    },

    updateTableEmptyConfig() {
      this.tableEmptyConf.keyword = this.logParams.keyword;
    },
  },
};
</script>

<style lang="scss" scoped>
    @import '~@/assets/css/mixins/ellipsis.scss';

    .result {
        position: relative;
    }

    .log-filter-box input[type="checkbox"] {
        appearance: none;
    }

    .logSelectPanel {
        display: inline-block;
        width: 112px;
    }

    .log-filter-box {
        padding: 15px 0 20px 0;

        .flex{
            display: flex;
        }

        .form-item {
            display: flex;
            .bk-label {
                line-height: 32px;
            }
            .bk-form-content {
                flex: 1;
                position: relative;
            }
        }

        /deep/ .bk-date-picker.long {
            width: 100%;
        }
    }

    .query-text {
        padding: 0 30px 0 10px;
        width: 304px;
        height: 32px;
        line-height: 32px;
        border-radius: 2px;
        border: solid 1px #c4c6cc;
        font-size: 12px;
        float: left;
        color: #666;
    }

    .query-date {
        position: relative;
        width: 269px;
        height: 36px;
        float: left;
    }

    .query-date .query-text {
        width: 227px;
        padding: 0 30px 0 10px;
        cursor: pointer;
        background: url(/static/images/query-date-icon.png) 270px center no-repeat;
        font-size: 13px;
    }

    .query-date:after {
        content: "";
        position: absolute;
        width: 1px;
        height: 34px;
        background: #ccc;
        top: 1px;
        left: 407px;
    }

    .chart-box {
        min-height: 150px;
        background: #FAFBFD;

        .chart-placeholder {
            background: #fafbfd;
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
        }
    }

    .result {
        margin: 20px 0;
    }

    .ps-log-table {
        &:before {
            display: none;
        }
        width: 100%;
        box-sizing: border-box;

        th {
            border-top: none;
            border-right: none;
        }
        td {
            font-size: 12px;
            color: #63656E;
        }
    }

    .ps-toggle-btn {
        margin-right: 4px;
        cursor: pointer;
        color: #C4C6CC;
        font-size: 12px;
    }

    .log-message {
        line-height: 20px;
        font-size: 13px;

        pre {
            position: relative;
            max-height: 40px;

            @include multiline-ellipsis;
        }
    }

    .detail-box {
        padding: 5px 0;
        background-color: #fafbfd;

        li {
            display: flex;
            padding: 0 10px;
            margin-top: 4px;
        }

        .key {
            display: block;
            min-width: 130px;
            line-height: 18px;
            text-align: right;
            padding-right: 10px;
            white-space: nowrap;
            margin-top: -3px;
        }

        .value {
            line-height: 18px;
            display: block;
            flex: 1;
            font-family: "Helvetica Neue", Helvetica, Tahoma, Arial, "Microsoft Yahei", "PingFang SC", STHeiTi, sans-serif;
        }
    }

    .ps-search-btn {
        margin: 13px 0 0 12px;
        width: 126px;
        line-height: 18px;
        vertical-align: middle;
    }

    .page-wrapper {
        margin-top: 15px;
        text-align: right;

        .bk-page {
            float: right;
        }
    }

    .clear-keyword-btn {
        position: absolute;
        right: 10px;
        top: 10px;
        cursor: pointer;
    }

    .default-time-text {
        position: absolute;
        left: 0;
        top: 0;
        margin: 1px;
        padding: 0 10px;
        width: 290px;
        height: 30px;
        background: #fff;
        z-index: 1;
        box-sizing: border-box;
        line-height: 30px;
        cursor: pointer;
        pointer-events: none;
        font-size: 12px;
    }

    .time-th {
        width: 200px;
    }

    .ps-checkbox-default {
        height: 16px;
        margin-top: 8px;
    }

    .log-main {
        display: flex;
        margin-top: 20px;

        .log-fields {
            width: 210px;
            min-width: 210px;
            background: #F5F6FA;
            border-radius: 2px;
            padding: 15px 10px 15px 20px;

            .title {
                font-size: 14px;
                color: #313238;
                margin-bottom: 18px;
            }

            .field-list {
                max-height: 1200px;
                overflow: auto;
                /deep/ .bk-form-checkbox {
                    display: block;
                    margin-bottom: 15px;

                    & + .bk-form-checkbox {
                        margin-left: 0;
                    }
                }
            }
        }

        .log-content {
            flex: 1;
            padding-left: 10px;
        }
    }
    .table-filters {
        margin: 10px 0 0 0;
        display: flex;
        align-items: center;

        .clear-filters-btn {
            cursor: pointer;
        }

        .filter-list {
            > li {
                background: #F0F1F5;
                border-radius: 2px;
                padding: 0 6px 0 6px;
                color: #63656E;
                font-size: 12px;
                display: inline-block;
                margin-right: 10px;

                .filter-value {
                    max-width: 300px;
                    overflow: hidden;
                    text-overflow: ellipsis;
                    white-space: nowrap;
                    display: inline-block;
                    line-height: 26px;
                    vertical-align: middle;

                    i {
                        font-style: normal;
                    }
                }

                .paasng-icon {
                    font-size: 20px;
                    vertical-align: middle;
                    cursor: pointer;
                }
            }
        }
    }

    .ps-log-header {
        background: #313238;
        border-bottom: 1px solid #000;
        padding: 10px 20px;
        margin-top: 20px;
        border-radius: 2px 2px 0 0;

        .text {
            margin-left: 5px;
            color: #fff;
            display: inline-block;
            vertical-align: middle;
            font-size: 12px;
        }
    }
    .ps-log-container {
        background: #313238;
        border-radius: 0 0 2px 2px;
        padding: 20px;
        color: red;
        font-size: 12px;
        line-height: 18px;
        overflow: auto;
        min-height: 300px;
        position: relative;
        margin-bottom: -40px;

        &::-webkit-scrollbar {
            width: 4px;
            background-color: lighten(transparent, 80%);
        }
        &::-webkit-scrollbar-thumb {
            height: 5px;
            border-radius: 2px;
            background-color: #63656e;
        }

        .ps-no-result {
            width: 80%;
            position: absolute;
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%);
        }

        .stream-log {
            display: flex;
            margin-bottom: 8px;
            font-family: Consolas, "source code pro", "Bitstream Vera Sans Mono", Consolas, Courier, monospace, "微软雅黑", "Arial";

            .pod-name {
                min-width: 95px;
                text-align: right;
                margin-right: 15px;
                color: #979BA5;
                cursor: pointer;

                &:hover {
                    color: #3a84ff;
                }
            }
            .message {
                flex: 1;
            }
        }
    }
    .scroll-loading {
        height: 40px;
        overflow: hidden;
        border-radius: 2px;
        margin-bottom: 10px;
    }
    .no-data {
        padding: 8px;
        text-align: center;
        background: #1d1e22;
        margin-bottom: 10px;
        border-radius: 2px;
    }
    .table-wrapper {
        width: auto;
        overflow: auto;
    }
    .tooltip-icon {
        cursor: pointer;
        vertical-align: middle;
    }
    .not-found {
        padding: 25px 0 10px 0;
        color: #333;
        font-size: 12px;
    }
    .caret-wrapper {
        display: inline-flex;
        flex-direction: column;
        align-items: center;
        width: 20px;
        height: 20px;
        flex: 20px 0 0;
        vertical-align: middle;
        cursor: pointer;
        overflow: visible;
        position: relative;
        position: absolute;
        right: 0;

        &.asc .sort-caret.ascending {
            border-bottom-color: #63656e;
        }

        &.desc .sort-caret.descending {
            border-top-color: #63656e;
        }

        .sort-caret {
            width: 0;
            height: 0;
            border: 5px solid transparent;
            position: absolute;

            &.ascending {
                border-bottom-color: #c0c4cc;
                top: -1px;
            }

            &.descending {
                border-top-color: #c0c4cc;
                bottom: -1px;
            }
        }
    }
    .log-wrapper {
        padding: 5px 0;
        background-color: #fafbfd;
        .log-item-info {
            display: flex;
            line-height: 18px;
            .left-title {
                width: 130px;
                text-align: right;
            }
            .left-val {
                flex: 1;
                padding-left: 10px;
            }
        }
    }
</style>
<style>
    .table-wrapper.log-table .bk-table .bk-table-body td.bk-table-expanded-cell {
        padding: 0;
    }
</style>
